//
// Created by T on 2018/11/14.
//

#ifndef __FIFO_H__
#define __FIFO_H__

#include "stm32f1xx_hal.h"
#include <string.h>

#define USE_MEMCPY 1 // FIXME:

struct fifo {
    size_t rd, wr;
    uint8_t dat[128];
};

/*
 * size 必须为 2 的幂
 */
#define ANONYMOUS_FIFO_DEF(size) \
    struct { \
        size_t rd, wr; \
        uint8_t dat[128]; \
    }

#define FIFO_SIZE(fifoptr)          (sizeof((fifoptr)->dat))
#define FIFO_MASK(fifoptr)          (FIFO_SIZE(fifoptr) - 1)
#define FIFO_INIT(fifoptr)          ((fifoptr)->rd = (fifoptr)->wr = 0)
#define FIFO_RESET(fifoptr)         ((fifoptr)->rd = (fifoptr)->wr)
#define FIFO_LEN(fifoptr)           ((fifoptr)->wr - (fifoptr)->rd)
#define FIFO_AVAIL(fifoptr)         (FIFO_SIZE(fifoptr) - FIFO_LEN(fifoptr))
#define FIFO_IS_FULL(fifoptr)       (FIFO_AVAIL(fifoptr) == 0)
#define FIFO_IS_EMPTY(fifoptr)      (FIFO_LEN(fifoptr) == 0)
#define FIFO_PUT_BYTE(fifoptr, d)   ((fifoptr)->dat[(fifoptr)->wr++ & FIFO_MASK(fifoptr)] = (d))
#define FIFO_GET_BYTE(fifoptr)      ((fifoptr)->dat[(fifoptr)->rd++ & FIFO_MASK(fifoptr)])

#define FIFO_SKIP(fifoptr, n) \
    do { \
        register size_t l = FIFO_LEN(fifoptr); \
        register size_t l2 = (n); \
        if (l > l2) \
            l = l2; \
        (fifoptr)->rd += l; \
    } while (0)

#if USE_MEMCPY

#define FIFO_MK_WR_ADDR(fifoptr)        ((fifoptr)->dat + ((fifoptr)->wr & FIFO_MASK(fifoptr)))
#define FIFO_MK_WR_TAIL_AVAIL(fifoptr)  (FIFO_SIZE(fifoptr) - ((fifoptr)->wr & FIFO_MASK(fifoptr)))
#define FIFO_MK_RD_ADDR(fifoptr)        ((fifoptr)->dat + ((fifoptr)->rd & FIFO_MASK(fifoptr)))
#define FIFO_MK_RD_TAIL_LEN(fifoptr)    (FIFO_SIZE(fifoptr) - ((fifoptr)->rd & FIFO_MASK(fifoptr)))

#define FIFO_PUT(fifoptr, dataptr, dlen, putlenptr)  \
    do { \
        register size_t ___len___ = FIFO_AVAIL(fifoptr); \
        register size_t ___n___ = FIFO_MK_WR_TAIL_AVAIL(fifoptr); \
        register uint8_t *___dat___ = (uint8_t *)(dataptr); \
        if (___len___ > (dlen)) \
            ___len___ = (dlen); \
        if (___n___) \
            memcpy(FIFO_MK_WR_ADDR(fifoptr), ___dat___, ___n___); \
        if (___n___ < ___len___) \
            memcpy((fifoptr)->dat, ___dat___ + ___n___, ___len___ - ___n___); \
        (fifoptr)->wr += ___len___; \
        if (putlenptr) \
            *(putlenptr) = ___len___; \
    } while (0)

#define FIFO_GET(fifoptr, bufptr, blen, getlenptr) \
    do { \
        register size_t ___len___ = FIFO_LEN(fifoptr); \
        register size_t ___n___ = FIFO_MK_RD_TAIL_LEN(fifoptr); \
        register uint8_t *___buf___ = (uint8_t *)(bufptr); \
        if (___len___ > (blen)) \
            ___len___ = (blen); \
        if (___n___) \
            memcpy(___buf___, FIFO_MK_RD_ADDR(fifoptr), ___n___); \
        if (___n___ < ___len___) \
            memcpy(___buf___ + ___n___, (fifoptr)->dat, ___len___ - ___n___); \
        (fifoptr)->rd += ___len___; \
        if (getlenptr) \
            *(getlenptr) = ___len___; \
    } while (0)

#else

#define FIFO_PUT(fifoptr, dataptr, dlen)  \
    do { \
        register size_t ___len___ = FIFO_AVAIL(fifoptr); \
        register uint8_t *___dat___ = (uint8_t *)(dataptr); \
        if (___len___ > (dlen)) \
            ___len___ = (dlen); \
        while (___len___--) \
            FIFO_PUT_BYTE((fifoptr), *___dat___++); \
    } while (0)

#define FIFO_GET(fifoptr, bufptr, blen) \
    do { \
        register size_t ___len___ = FIFO_LEN(fifoptr); \
        register uint8_t *___buf___ = (uint8_t *)(bufptr); \
        if (___len___ > (blen)) \
            ___len___ = (blen); \
        while (___len___--) \
            *___buf___++ = FIFO_GET_BYTE(fifoptr); \
    } while (0)

#endif


#endif //PAC_FIFO_H
